Get rid of navigation region in GtkMenu
authorBenjamin Otte <otte@redhat.com>
Wed, 16 Jun 2010 11:14:01 +0000 (13:14 +0200)
committerBenjamin Otte <otte@redhat.com>
Mon, 28 Jun 2010 12:19:18 +0000 (14:19 +0200)
This completes the move to get rid of using a GdkRegion for the
navigation region and the only user of gdk_region_polygon(). We keep
track of the triangle and compute in/out points ourselves now.

Unfortunately the DRAW_STAYUP_TRIANGLES debugging code doesn't work
using cairo, so I removed it completely.

gtk/gtkmenu.c
gtk/gtkmenu.h

index 3835e7300af0e739caa8fce50c7646a9ade798bd..c9b23827a7da6c5491738ee01d9b6213e3100d1e 100644 (file)
@@ -94,6 +94,12 @@ struct _GtkMenuPrivate
   GtkStateType lower_arrow_state;
   GtkStateType upper_arrow_state;
 
+  /* navigation region */
+  int navigation_x;
+  int navigation_y;
+  int navigation_width;
+  int navigation_height;
+
   guint have_layout           : 1;
   guint seen_item_enter       : 1;
   guint have_position         : 1;
@@ -3325,6 +3331,16 @@ definitely_within_item (GtkWidget *widget,
     check_threshold (widget, 0, h - 1, x, y);
 }
 
+static gboolean
+gtk_menu_has_navigation_triangle (GtkMenu *menu)
+{
+  GtkMenuPrivate *priv;
+
+  priv = gtk_menu_get_private (menu);
+
+  return priv->navigation_height && priv->navigation_width;
+}
+
 static gboolean
 gtk_menu_motion_notify (GtkWidget      *widget,
                         GdkEventMotion *event)
@@ -3366,7 +3382,7 @@ gtk_menu_motion_notify (GtkWidget      *widget,
   if (definitely_within_item (menu_item, event->x, event->y))
     menu_shell->activate_time = 0;
 
-  need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter);
+  need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->ignore_enter);
 
   /* Check to see if we are within an active submenu's navigation region
    */
@@ -4066,11 +4082,13 @@ gtk_menu_leave_notify (GtkWidget        *widget,
 static void 
 gtk_menu_stop_navigating_submenu (GtkMenu *menu)
 {
-  if (menu->navigation_region) 
-    {
-      gdk_region_destroy (menu->navigation_region);
-      menu->navigation_region = NULL;
-    }  
+  GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+
+  priv->navigation_x = 0;
+  priv->navigation_y = 0;
+  priv->navigation_width = 0;
+  priv->navigation_height = 0;
+
   if (menu->navigation_timeout)
     {
       g_source_remove (menu->navigation_timeout);
@@ -4119,49 +4137,48 @@ gtk_menu_navigating_submenu (GtkMenu *menu,
                             gint     event_x,
                             gint     event_y)
 {
-  if (menu->navigation_region)
-    {
-      if (gdk_region_point_in (menu->navigation_region, event_x, event_y))
-       return TRUE;
-      else
-       {
-         gtk_menu_stop_navigating_submenu (menu);
-         return FALSE;
-       }
-    }
-  return FALSE;
-}
+  GtkMenuPrivate *priv;
+  int width, height;
 
-#undef DRAW_STAY_UP_TRIANGLE
+  if (!gtk_menu_has_navigation_triangle (menu))
+    return FALSE;
 
-#ifdef DRAW_STAY_UP_TRIANGLE
+  priv = gtk_menu_get_private (menu);
+  width = priv->navigation_width;
+  height = priv->navigation_height;
 
-static void
-draw_stay_up_triangle (GdkWindow *window,
-                      GdkRegion *region)
-{
-  /* Draw ugly color all over the stay-up triangle */
-  GdkColor ugly_color = { 0, 50000, 10000, 10000 };
-  GdkGCValues gc_values;
-  GdkGC *ugly_gc;
-  GdkRectangle clipbox;
-
-  gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
-  ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW);
-  gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color);
-  gdk_gc_set_clip_region (ugly_gc, region);
-
-  gdk_region_get_clipbox (region, &clipbox);
-  
-  gdk_draw_rectangle (window,
-                     ugly_gc,
-                     TRUE,
-                     clipbox.x, clipbox.y,
-                     clipbox.width, clipbox.height);
-  
-  g_object_unref (ugly_gc);
+  /* check if x/y are in the triangle spanned by the navigation parameters */
+
+  /* 1) Move the coordinates so the triangle starts at 0,0 */
+  event_x -= priv->navigation_x;
+  event_y -= priv->navigation_y;
+
+  /* 2) Ensure both legs move along the positive axis */
+  if (width < 0)
+    {
+      event_x = -event_x;
+      width = -width;
+    }
+  if (height < 0)
+    {
+      event_y = -event_y;
+      height = -height;
+    }
+
+  /* 3) Check that the given coordinate is inside the triangle. The formula
+   * is a transformed form of this formula: x/w + y/h <= 1
+   */
+  if (event_x >= 0 && event_y >= 0 &&
+      event_x * height + event_y * width <= width * height)
+    {
+      return TRUE;
+    }
+  else
+    {
+      gtk_menu_stop_navigating_submenu (menu);
+      return FALSE;
+    }
 }
-#endif
 
 static void
 gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
@@ -4174,13 +4191,15 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
   gint submenu_bottom = 0;
   gint width = 0;
   gint height = 0;
-  GdkPoint point[3];
   GtkWidget *event_widget;
   GtkMenuPopdownData *popdown_data;
+  GtkMenuPrivate *priv;
 
   g_return_if_fail (menu_item->submenu != NULL);
   g_return_if_fail (event != NULL);
   
+  priv = gtk_menu_get_private (menu);
+
   event_widget = gtk_get_event_widget ((GdkEvent*) event);
   
   gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
@@ -4197,43 +4216,43 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
       
       gtk_menu_stop_navigating_submenu (menu);
 
+      /* The navigation region is the triangle closest to the x/y
+       * location of the rectangle. This is why the width or height
+       * can be negative.
+       */
+
       if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
        {
          /* right */
-         point[0].x = event->x_root;
-         point[1].x = submenu_left;
+          priv->navigation_x = submenu_left;
+          priv->navigation_width = event->x_root - submenu_left;
        }
       else
        {
          /* left */
-         point[0].x = event->x_root + 1;
-         point[1].x = submenu_right;
+          priv->navigation_x = submenu_right;
+          priv->navigation_width = event->x_root - submenu_right;
        }
 
       if (event->y < 0)
        {
          /* top */
-         point[0].y = event->y_root;
-         point[1].y = submenu_top - NAVIGATION_REGION_OVERSHOOT;
+          priv->navigation_y = event->y_root;
+          priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT;
 
-         if (point[0].y <= submenu_top)
+         if (priv->navigation_height >= 0)
            return;
        }
       else
        {
          /* bottom */
-         point[0].y = event->y_root + 1;
-         point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT;
+          priv->navigation_y = event->y_root;
+          priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT;
 
-         if (point[0].y >= submenu_bottom)
+         if (priv->navigation_height <= 0)
            return;
        }
 
-      point[2].x = point[1].x;
-      point[2].y = point[0].y;
-
-      menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
-
       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)),
                    "gtk-menu-popdown-delay", &popdown_delay,
                    NULL);
@@ -4247,11 +4266,6 @@ gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
                                                                gtk_menu_stop_navigating_submenu_cb,
                                                                popdown_data,
                                                                (GDestroyNotify) g_free);
-
-#ifdef DRAW_STAY_UP_TRIANGLE
-      draw_stay_up_triangle (gdk_get_default_root_window(),
-                            menu->navigation_region);
-#endif
     }
 }
 
index 0929472af72d3a8a2381f840f198c324bc15c136..4e4746fc0697a7d4eeb71563e966ace92a6f8be9 100644 (file)
@@ -92,7 +92,7 @@ struct _GtkMenu
   /* When a submenu of this menu is popped up, motion in this
    * region is ignored
    */
-  GdkRegion *GSEAL (navigation_region);
+  GdkRegion *GSEAL (navigation_region); /* unused */
   guint GSEAL (navigation_timeout);
 
   guint GSEAL (needs_destruction_ref_count) : 1;